unit unitRemoteMemoryWalker;

interface

const
  RMW_BufferLen = $4000;
  RMW_BeginAddr = $40000;
  RMW_EndAddr = $800000;

type
  TRemoteMemoryWalker = class
  private
    FProcHandle: THandle;
    FBuffer: PByte;
    FBufferLen, FBeginAddr, FEndAddr: Cardinal;
  protected
    function GetOpened: Boolean;
  public
    constructor Create(BufferLen: Cardinal = RMW_BufferLen;
      BeginAddress: Cardinal = RMW_BeginAddr;
      EndAddress:Cardinal = RMW_EndAddr);
    destructor Destroy;

    function Open(WindowCaption, WindowClass: String): Boolean;
    procedure Close;

    function Search(const Pattern: AnsiString;
      out Addrs: array of Cardinal; Matches: array of AnsiString;
      const UsingMask: Boolean = False; StopCount: Integer = 0): Boolean; overload;
    function Search(const Pattern: WideString;
      out Addrs: array of Cardinal; Matches: array of WideString;
      const UsingMask: Boolean = False; StopCount: Integer = 0): Boolean; overload;

    property Opened: Boolean read GetOpened;
  end;


implementation

uses Windows, SysUtils;

{*
  ByteSearch AnsiString version;
*}
function ByteSearchA(Key, Buffer: PAnsiChar;
  KeyLen, BufferLen: Cardinal; out Pos: Cardinal; StartPos: Cardinal = 0;
  UsingMask: Boolean = False): Boolean;
function CheckMatch(Key, Val: PAnsiChar; UsingMask: Boolean = False): Boolean;
begin
  if not UsingMask then
    Result := Key^ = Val^ else
  begin
    case Byte(Key^) of
    Byte(AnsiChar('?')):
      Result := ((Val^ >= AnsiChar('a')) and (Val^ <= AnsiChar('z'))) or
                ((Val^ >= AnsiChar('A')) and (Val^ <= AnsiChar('Z')));
    Byte(AnsiChar('#')):
      Result := (Val^ >= AnsiChar('0')) and (Val^ <= AnsiChar('9'));
    else
      Result := Key^ = Val^;
    end;
  end;
end;
var
  idx, idy, iMatchCount: Cardinal;
begin
  Result := False;
  if (not Assigned(Key)) or (not Assigned(Buffer)) or (KeyLen <= 0) or
    (BufferLen <= 0) or (KeyLen > BufferLen) or (StartPos >= BufferLen) then
    Exit;

  for idx := StartPos to BufferLen - KeyLen do
  begin
    if not CheckMatch(Key, Buffer + idx, UsingMask) then Continue else
    begin
      iMatchCount := 1;
      if KeyLen = iMatchCount then Break;
      
      for idy := 1 to KeyLen - 1 do
        if not CheckMatch(Key + idy, Buffer + idx + idy, UsingMask) then Break else
        begin
          Inc(iMatchCount);
        end;

      if iMatchCount = KeyLen then Break;
    end;
  end;

  if iMatchCount = KeyLen then
  begin
    Result := True;
    Pos := idx;
  end else
  begin
    Result := False;
    Pos := BufferLen + 1;
  end;
end;

{*
  ByteSearch WideString version;
*}
function ByteSearchW(Key, Buffer: PWideChar;
  KeyLen, BufferLen: Cardinal; out Pos: Cardinal; StartPos: Cardinal = 0;
  UsingMask: Boolean = False): Boolean;
function CheckMatch(Key, Val: PWideChar; UsingMask: Boolean = False): Boolean;
begin
  if not UsingMask then
    Result := Key^ = Val^ else
  begin
    case Byte(Key^) of
    Byte(WideChar('?')):
      Result := ((Val^ >= WideChar('a')) and (Val^ <= WideChar('z'))) or
                ((Val^ >= WideChar('A')) and (Val^ <= WideChar('Z')));
    Byte(WideChar('#')):
      Result := (Val^ >= WideChar('0')) and (Val^ <= WideChar('9'));
    else
      Result := Key^ = Val^;
    end;
  end;
end;
var
  idx, idy, iMatchCount: Cardinal;
begin
  Result := False;
  if (not Assigned(Key)) or (not Assigned(Buffer)) or (KeyLen <= 0) or
    (BufferLen <= 0) or (KeyLen > BufferLen) or (StartPos >= BufferLen) then
    Exit;

  for idx := StartPos to BufferLen - KeyLen do
  begin
    if not CheckMatch(Key, Buffer + idx, UsingMask) then Continue else
    begin
      iMatchCount := 1;
      if KeyLen = iMatchCount then Break;
      
      for idy := 1 to KeyLen - 1 do
        if not CheckMatch(Key + idy, Buffer + idx + idy, UsingMask) then Break else
        begin
          Inc(iMatchCount);
        end;

      if iMatchCount = KeyLen then Break;
    end;
  end;

  if iMatchCount = KeyLen then
  begin
    Result := True;
    Pos := idx;
  end else
  begin
    Result := False;
    Pos := BufferLen + 1;
  end;
end;

{ TRemoteMemoryWalker }

procedure TRemoteMemoryWalker.Close;
begin
  if Opened then CloseHandle(FProcHandle);
end;

constructor TRemoteMemoryWalker.Create(BufferLen, BeginAddress,
  EndAddress: Cardinal);
begin
  FBufferLen := BufferLen;
  FBeginAddr := BeginAddress;
  FEndAddr := EndAddress;
end;

destructor TRemoteMemoryWalker.Destroy;
begin
  Close;
  inherited;
end;

function TRemoteMemoryWalker.GetOpened: Boolean;
begin
  Result := FProcHandle > 0;
end;

function TRemoteMemoryWalker.Open(WindowCaption,
  WindowClass: String): Boolean;
var
  vClass, vCaption: PAnsiChar;
  vHandle, vWnd, vProc, vThread: Cardinal;
begin
  FProcHandle := 0;
  if '' = Trim(WindowClass) then
    vClass := nil else
    vClass := PAnsiChar(WindowClass);
  if '' = Trim(WindowCaption) then
    vCaption := nil else
    vCaption := PAnsiChar(WindowCaption);
  vWnd := FindWindow(vClass, vCaption);
  if IsWindow(vWnd) then
  begin
    GetWindowThreadProcessId(vWnd, vProc);
    FProcHandle := OpenProcess(PROCESS_VM_READ, False, vProc);
  end;
  Result := Opened;
end;

function TRemoteMemoryWalker.Search(const Pattern: WideString;
  out Addrs: array of Cardinal; Matches: array of WideString;
  const UsingMask: Boolean; StopCount: Integer): Boolean;
const
  ArrayCapacity = 1024;
var
  iAddrCount: Cardinal;
begin
  if not Opened then Exit;



end;

function TRemoteMemoryWalker.Search(const Pattern: AnsiString;
  out Addrs: array of Cardinal; Matches: array of AnsiString;
  const UsingMask: Boolean; StopCount: Integer): Boolean;
begin

end;

end.
